package be.pluyckx.batteryprogress.data.history.snapshot;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import android.util.SparseArray;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;

import be.pluyckx.batteryprogress.R;
import be.pluyckx.batteryprogress.application.Constants;
import be.pluyckx.batteryprogress.database.BatteryLevelFileStorage;
import be.pluyckx.batteryprogress.utils.StringUtils;

/**
 * Created by Philip on 26/08/13.
 */
public class ProcessSnapshot implements BatteryLevelFileStorage.FileWritable {
  private static final String TAG = "ProcessSnapshot";
  private final SparseArray<ProcessItem> mProcesses;
  private String mCurrentActivity = null;

  public ProcessSnapshot() {
    mProcesses = new SparseArray<ProcessItem>();
  }

  public void makeSnapshot(final Context context, final Callback callback) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        mProcesses.clear();

        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        PackageManager packageManager = context.getPackageManager();
        List<ActivityManager.RunningAppProcessInfo> apps = manager.getRunningAppProcesses();

        for (ActivityManager.RunningAppProcessInfo app : apps) {
          try {
            ApplicationInfo info = packageManager.getApplicationInfo(app.processName, PackageManager.GET_META_DATA);
            mProcesses.put(app.pid, new ProcessItem(app.pid,
                packageManager.getApplicationLabel(info).toString()));
          } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            mProcesses.put(app.pid, new ProcessItem(app.pid, app.processName));
          }
          if (Constants.DEBUG) {
            Log.d(TAG, "Process found: " + app.pid + " " + app.processName);
          }
        }

        List<ActivityManager.RunningServiceInfo> services = manager.getRunningServices(100);
        for (ActivityManager.RunningServiceInfo service : services) {
          ServiceItem s = new ServiceItem(service.pid, service.service.getClassName());
          ProcessItem p = mProcesses.get(service.pid);
          if (p != null) {
            p.addService(s);
          } else {
            p = new ProcessItem(s.getPid(), context.getResources().getString(R.string.unknown));
            p.addService(s);
            mProcesses.put(p.getProcessId(), p);
          }
        }
        List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
        if (tasks.size() == 1) {
          if (tasks.get(0).topActivity != null) {
            mCurrentActivity = tasks.get(0).topActivity.getClassName();
          } else {
            mCurrentActivity = null;
          }
        }

        if (callback != null) {
          callback.onSnapshotReady(ProcessSnapshot.this);
        }
      }
    }).start();
  }

  @Override
  public int streamSize() {
    //int: process id + str length
    int size = StringUtils.lengthUTF(mCurrentActivity, true) + Integer.SIZE / 8;

    for (int i = 0; i < mProcesses.size(); i++) {
      size += mProcesses.valueAt(i).streamSize();
    }

    return size;
  }

  @Override
  public void write(DataOutputStream dos) throws IOException {
    dos.writeUTF(mCurrentActivity);
    dos.writeInt(mProcesses.size());
    for (int i = 0; i < mProcesses.size(); i++) {
      mProcesses.valueAt(i).write(dos);
    }
  }

  public ProcessSnapshot(DataInputStream dis) throws IOException {
    mCurrentActivity = dis.readUTF();
    int size = dis.readInt();
    mProcesses = new SparseArray<ProcessItem>(size);

    for (int i = 0; i < size; i++) {
      ProcessItem item = new ProcessItem(dis);
      mProcesses.put(item.getProcessId(), item);
    }
  }

  public int count() {
    return mProcesses.size();
  }

  public ProcessItem get(int index) {
    return mProcesses.valueAt(index);
  }

  public ProcessItem getByPid(int pid) {
    return mProcesses.get(pid);
  }

  public String getCurrentActivity() {
    return mCurrentActivity;
  }

  public static interface Callback {
    void onSnapshotReady(ProcessSnapshot snapshot);
  }
}
